home *** CD-ROM | disk | FTP | other *** search
/ Delphi Informant Complete 1995 - 2000 / Delphi Informant Complete 1995 to 2000.iso / Delphi Informant Magazine Complete Works SOURCE CODE 1998.rar / 1998 / Jul / di9807jp / SFTPCLIENT / CMsgThrd.pas < prev    next >
Pascal/Delphi Source File  |  1998-02-20  |  11KB  |  410 lines

  1. (*
  2.  Message Thread unit developed for Delphi Informant article by John Penman, 23 January 1998
  3.  SFTP Client message thread
  4. *)
  5.  
  6. unit CMsgThrd;
  7.  
  8. interface
  9.  
  10. uses
  11.  Classes, extctrls, Windows, Winsock2;
  12.  
  13. type
  14.  
  15. // Simple state machine
  16.   TStates = (stMsg, stData, stError, stDead);
  17.  
  18. // Info from the main form
  19.  
  20.   TRequest = record
  21.               Password,
  22.               Port,
  23.               UserName,
  24.               FileName,
  25.               MachineName,
  26.               HostName : String;
  27.              end;
  28.  
  29. // Event record
  30.  
  31.   TConnData = record
  32.                NumEvents : DWORD;
  33.                EventArray : array[1..WSA_MAXIMUM_WAIT_EVENTS] of WSAEvent;
  34.               end;
  35.  
  36. // The Message thread class
  37.  
  38.   TMsgThrd = class(TThread)
  39.   private
  40.     { Private declarations }
  41.   protected
  42.     EventMsg,
  43.     EventData              : WSAEVENT;
  44.     Done                   : Boolean;
  45.     lpNetworkEvents        : PWSANETWORKEVENTS;
  46.     ConnData               : TConnData;
  47.     wsaData                : TWSADATA;
  48.     Host                   : PHostent;
  49.     CurrentIPAddr,
  50.     DataFileName,
  51.     Msg,
  52.     OldMsgPort             : String;
  53.     sktData,
  54.     sktMsg                 : TSocket;
  55.     h_addr                 : pchar;
  56.     ClientAddr, HostAddr   : TSockAddrIn;
  57.     Buffers                : array[0..MAXGETHOSTSTRUCT-1] of char;
  58.     Request                : TRequest;
  59.     ResTimer               : TTimer;
  60.     TimeOutValue           : Integer;
  61.     RequestMsg             : String;
  62.     procedure Execute; override;
  63.     procedure Update;
  64.     procedure EnableBtn;
  65.     procedure Resolve;
  66.     procedure SendMsg(Msg : String);
  67.     procedure HandleSocketEvent;
  68.     procedure OnMsgThrdDone(Sender : TObject);
  69.     procedure OnTimeOut(Sender : TObject);
  70.   public
  71.     Finished : Boolean;
  72.     constructor Create(Requests : TRequest; TimerSetting : Integer);
  73.   end;
  74.  
  75. var
  76.   State : TStates;
  77.  
  78. implementation
  79.  
  80. uses CDataThrd, Dialogs, Main, SysUtils;
  81.  
  82. { TMsgThrd }
  83.  
  84. procedure TMsgThrd.Update;
  85. begin
  86.  frmMain.memStatusMsg.Lines.Add(Msg);
  87. end;
  88.  
  89. procedure TMsgThrd.EnableBtn;
  90. begin
  91.  frmMain.bbtnGetFile.Enabled := TRUE;
  92. end;
  93.  
  94. procedure TMsgThrd.Resolve;
  95. begin
  96. // Resolve hostname
  97.   Host := gethostbyname(pchar(Request.HostName));
  98.   if Host = NIL then     
  99.   begin
  100.    Msg := Concat('Failed to find host ',Request.HostName);
  101.    Synchronize(Update);
  102.    Synchronize(EnableBtn);
  103.    State := stError;
  104.    Done := TRUE;
  105.    Exit;
  106.   end;
  107.   Msg := Concat('Host ' + frmMain.edHostname.Text, ' found...');
  108.   Synchronize(Update);
  109.   Move(Host^.h_addr_list^, h_addr, SizeOf(Host^.h_addr_list^));
  110.   with HostAddr.sin_addr do
  111.   begin
  112.    S_un_b.s_b1 := h_addr[0];
  113.    S_un_b.s_b2 := h_addr[1];
  114.    S_un_b.s_b3 := h_addr[2];
  115.    S_un_b.s_b4 := h_addr[3];
  116.   end;
  117.   HostAddr.sin_family := AF_INET;
  118.   HostAddr.sin_port   := htons(MsgPort);
  119. // We got this far, so we send a message ...
  120.   SendMsg(RequestMsg);
  121. end;
  122.  
  123.  
  124. procedure TMsgThrd.SendMsg(Msg : String);
  125. var
  126.  Buff : PWSABUF;
  127.  BuffCount,
  128.  Flags,
  129.  Len,
  130.  Res,
  131.  NoBytesSent : Integer;
  132. begin
  133.   Len := SizeOf(HostAddr);
  134.   Buff := NIL;
  135.   try
  136.    Buff := AllocMem(SizeOf(Buffers));
  137.    Buff.Buf := Buffers;
  138.    Buff.Buf := PChar(Msg);
  139.    Buff.Len := SizeOf(Buffers);
  140.    Flags := 0;
  141.    BuffCount := 1;
  142.    ResTimer.Enabled  := TRUE;
  143.  
  144. // Send the message ...
  145.  
  146.    Res := WSASendTo(sktMsg, Buff, BuffCount, @NoBytesSent, Flags, @HostAddr, Len, NIL, NIL);
  147.    if Res = SOCKET_ERROR then
  148.    begin
  149.     Msg := Concat('Failed to send.  Error ', IntToStr(WSAGetLastError));
  150.     Freemem(Buff, SizeOf(Buffers));
  151.     Synchronize(Update);
  152.     State := stError;
  153.     Done := TRUE;
  154.     Exit;
  155.    end;
  156.   finally
  157.    Freemem(Buff, SizeOf(Buffers));
  158.   end;
  159. end;
  160.  
  161. procedure TMsgThrd.HandleSocketEvent;
  162. var
  163.  Res : Integer;
  164.  Buff : PWSABUF;
  165.  Flags, NoBytes,
  166.  Size, AddrStrSize, Error : Integer;
  167.  AddrStr : PChar;
  168. begin
  169.  if State = stError then
  170.   Exit;
  171.  Flags := 0;
  172.  Res := WSAEnumNetworkEvents(sktMsg, EventMsg, @Buffers[0]);
  173.  if Res = SOCKET_ERROR then
  174.  begin
  175.   Msg := Concat('Call to WSAEnumNetworkEvents failed. Error ', IntToStr(WSAGetLastError));
  176.   Synchronize(Update);
  177.   Done := TRUE;
  178.   Exit;
  179.  end;
  180.  lpNetworkEvents := PWSANETWORKEVENTS(@Buffers[0]);
  181. // Decipher Network events...
  182.  with lpNetworkEvents^ do
  183.  begin
  184. // Is this a FD_READ event?
  185.   if (lNetworkEvents and FD_READ) = FD_READ then
  186.   begin
  187.    if iErrorCode[1] = WSAENETDOWN then
  188.    begin
  189.     Msg := 'Network down...';
  190.     Synchronize(Update);
  191.    end;
  192.    Size := SizeOf(HostAddr);
  193.    Msg := 'FD_READ...';
  194.    Synchronize(Update);
  195.    Buff := NIL;// This is a dummy to avoid the warning message from the compiler
  196.    try
  197.     Buff := AllocMem(SizeOf(Buffers));
  198.     Buff.Buf := Buffers;
  199.     Buff.len := SizeOf(Buffers);
  200.     Res := WSARecvFrom(sktMsg, Buff, 1, @NoBytes, @Flags,
  201.                        @HostAddr, @Size, NIL, NIL);
  202.     if Res = SOCKET_ERROR then
  203.     begin
  204.      Error := WSAGetLastError;
  205.      if Error <> WSAEWOULDBLOCK then
  206.      begin
  207.       Msg := Concat('Call to WSARecvFrom failed. Error ',IntToStr(Error));
  208.       Synchronize(Update);
  209.       Done := TRUE;
  210.       Exit;
  211.      end;
  212.     end else
  213.     begin
  214.      if State = stMsg then
  215.      begin
  216.       Msg := Concat('Message : ', String(Buff.Buf));
  217.       Synchronize(Update);
  218.       AddrStr := NIL;
  219.       try
  220.        AddrStr := StrAlloc(MAXGETHOSTSTRUCT);
  221.        AddrStrSize := MAXGETHOSTSTRUCT;
  222.        Res := WSAAddressToString(@HostAddr, SizeOf(HostAddr), NIL, AddrStr, @AddrStrSize);
  223.        if Res = SOCKET_ERROR then
  224.        begin
  225.         Msg := Concat('Call to WSAAddressToString failed. Error ', IntToStr(WSAGetLastError));
  226.         Synchronize(Update);
  227.        end;
  228.        Msg := Concat('Message from host ', String(AddrStr));
  229.        Synchronize(Update);
  230.        CurrentIPAddr := copy(String(AddrStr), 1, Pos(':', String(AddrStr))-1);
  231.       finally
  232.        StrDispose(AddrStr);
  233.       end;
  234. // Parse message from the host
  235.       Msg := String(Buff.buf);
  236.       Synchronize(Update);
  237.       if Pos('OK',UpperCase(String(Buff.Buf))) > 0 then
  238.       begin // Okay, set up port for listening
  239.        Msg := 'Now setting up port for data transfer...';
  240.        Synchronize(Update);
  241.        State := stData;
  242.        thrdData := TDataThrd.Create(StrToInt(Request.Port), Request.FileName);
  243.        Done := TRUE;
  244.       end else
  245.       begin
  246. // Any other response is an error, so handle it gracefully
  247.        Msg := 'Error. Cannot retrieve file.';
  248.        Synchronize(Update);
  249.        State := stError;
  250.        Synchronize(EnableBtn);
  251.        Done := TRUE;
  252.       end;
  253.      end;
  254.      if State = stData then
  255.      begin
  256.       Msg := 'Transferring data...';
  257.       Synchronize(Update);
  258.      end;
  259.     end;
  260.    finally
  261.     FreeMem(Buff);
  262.    end;
  263.   end;
  264. // Is this a FD_WRITE event?
  265.   if (lNetworkEvents and FD_WRITE) = FD_WRITE then
  266.   begin
  267.    if iErrorCode[2] = WSAENETDOWN then
  268.    begin
  269.     Msg := 'Network down...';
  270.     Synchronize(Update);
  271.    end else
  272.    begin
  273.     Msg := 'FD_WRITE...';
  274.     Synchronize(Update);
  275.    end;
  276.   end;
  277.  end;
  278. end;
  279.  
  280. constructor TMsgThrd.Create(Requests : TRequest; TimerSetting : Integer);
  281. var
  282.  Res        : Integer;
  283. begin
  284.  inherited Create(TRUE);
  285.  FreeOnTerminate   := TRUE;
  286.  OnTerminate       := OnMsgThrdDone;
  287.  TimeOutValue      := TimerSetting;
  288.  Done              := FALSE;
  289. // Set the Timer ...
  290.  ResTimer          := TTimer.Create(NIL);
  291.  ResTimer.Interval := TimeOutValue;
  292.  ResTimer.OnTimer  := OnTimeOut;
  293.  ResTimer.Enabled  := FALSE;
  294. // Decode Request record to build the request message ...
  295.  Request := Requests;
  296.  with Request do
  297.  begin
  298.   RequestMsg := ConCat(UserName,':',Password,':',
  299.                        MachineName,':',Port,':',FileName);
  300.  end;
  301.  State := stMsg;
  302. // Set up the message socket ...
  303.  sktMsg := WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NIL, 0, 0);
  304.  if sktMsg = SOCKET_ERROR then
  305.  begin
  306.   Msg := Concat('Failed to create socket Error ', IntToStr(WSAGetLastError));
  307.   Synchronize(Update);
  308.   State := stError;
  309.   Done := TRUE;
  310.   Exit;
  311.  end; // AllocMem
  312. // Creates events ...
  313.  EventMsg := CreateEvent(NIL,